﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GenericDbObject.cs" company="MyCoreLib">
//   Copyright (C) 2014 Alexey Evlampiev. All rights reserved.
// </copyright>
// <summary>
//   Defines the GenericDbObject type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Text.RegularExpressions;

namespace MyCoreLib.Automation.Data.SqlClient
{
    /// <summary>
    /// Generic database object representation.
    /// </summary>
    public class GenericDbObject : IDbObject, IEquatable<IDbObject>, IEquatable<GenericDbObject>
    {
        private const string Dbo = "dbo";

        private static readonly Regex FullNameRegex;

        static GenericDbObject()
        {
            string fullNamePattern = @"(?xis)^\s*
                (?:
                    (?:@schema ** @name) |
                    (?:\[@schema\] ** \[@name\]) |
                    (?:@schema ** \[@name\]) |
                    (?:\[@schema\] ** @name) |
                    (?:@name) |
                    (?:\[@name\])
                )\s*$"
                .Replace("@schema", @"(?:\s*  (?<schema>\w+) \s*)")
                .Replace("@name", @"(?:\s*  (?<name>  \w+ (?:\s+\w+)* ) \s*)")
                .Replace("**", @"(?:\s*\.\s*)");

            fullNamePattern = Regex.Replace(fullNamePattern, @"\s+", String.Empty);
            FullNameRegex = new Regex(fullNamePattern, RegexOptions.Compiled);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericDbObject"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <exception cref="System.ArgumentException">if (string.IsNullOrWhiteSpace(name))</exception>
        /// <exception cref="System.FormatException">name</exception>
        public GenericDbObject(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentException("if (string.IsNullOrWhiteSpace(name))");
            }
            var match = FullNameRegex.Match(name);
            if (match.Success == false)
            {
                throw new FormatException("name");
            }
            Schema = match.Groups["schema"].Success ? match.Groups["schema"].Value : Dbo;
            Name = match.Groups["name"].Value;
            FullName = BuildFullName();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericDbObject"/> class.
        /// </summary>
        /// <param name="schema">The schema.</param>
        /// <param name="name">The name.</param>
        public GenericDbObject(string schema, string name) 
            : this(string.Format("{0}.{1}", schema, name))
        {            
        }

        public GenericDbObject(IDbObject dbObject)
        {
            if (string.IsNullOrWhiteSpace(dbObject.Schema))
            {
                var fromName = new GenericDbObject(dbObject.Name);
                this.Schema = fromName.Schema;
                this.Name = fromName.Name;
            }
            else
            {
                var fromName = new GenericDbObject(string.Format("{0}.{1}", dbObject.Schema, dbObject.Name));
                this.Schema = fromName.Schema;
                this.Name = fromName.Name;
            }
            FullName = BuildFullName();
        }

        /// <summary>
        /// Builds the database objects full name.
        /// </summary>
        /// <returns></returns>
        private string BuildFullName()
        {
            return string.Format("[{0}].[{1}]", Schema, Name);
        }

        /// <summary>
        /// Gets the database objects schema.
        /// </summary>
        /// <value>
        /// The schema name.
        /// </value>
        public string Schema
        {
            get; private set;
        }

        /// <summary>
        /// Gets the database objects name.
        /// </summary>
        /// <value>
        /// The database objects name.
        /// </value>
        public string Name { get; private set; }

        /// <summary>
        /// The database objects full name
        /// </summary>
        public readonly string FullName;

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return FullName;
        }

        /// <summary>
        /// Determines whether the given string is a valid database objects name.
        /// </summary>
        /// <param name="dbObjectName">Name of the database object.</param>
        /// <returns></returns>
        public static bool IsValidName(string dbObjectName)
        {
            return FullNameRegex.IsMatch(dbObjectName);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public bool Equals(IDbObject other)
        {
            var obj = other as GenericDbObject ?? new GenericDbObject(other);
            return this.Equals(other);
        }

        public bool Equals(GenericDbObject other)
        {
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            return this.FullName.Equals(other.FullName, StringComparison.OrdinalIgnoreCase);
        }
    }
}
